using System.Collections.Specialized;
using System.Windows.Data;
using System.Linq;

namespace System.Windows.Controls
{
    /// <summary>
    /// Provides a hierarchical selectable item for the <see cref="TreeGrid"/> control
    /// </summary>
    public class TreeGridItem : ItemsControl
    {
        #region DependencyProperties
        /// <summary>
        /// Identifies the <see cref="IsExpanded"/> dependency property
        /// </summary>
        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded", typeof (bool), typeof (TreeGridItem),
                                        new PropertyMetadata(IsExpandedChanged));

        private static void IsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TreeGridItem) d).UpdateExpandState((bool) e.OldValue, (bool) e.NewValue);
        }

        /// <summary>
        /// Identifies the <see cref="IsSelected"/> dependency property
        /// </summary>
        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.Register("IsSelected", typeof (bool), typeof (TreeGridItem),
                                        new PropertyMetadata(IsSelectedChanged));

        private static void IsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        /// <summary>
        /// Identifies the <see cref="Data"/> dependency property
        /// </summary>
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof (object), typeof (TreeGridItem),
                                        new PropertyMetadata(DataChanged));

        private static void DataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TreeGridItem) d).DataContext = e.NewValue;
        }

        /// <summary>
        /// Identifies the <see cref="Visibility"/> dependency property
        /// </summary>
        public new static DependencyProperty VisibilityProperty =
            DependencyProperty.Register("Visibility", typeof (Visibility), typeof (TreeGridItem),
                                        new PropertyMetadata(OnVisibilityChanged));

        private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var item = (TreeGridItem) d;
            var vis = (Visibility) e.NewValue;
            if (item.IsExpanded && vis == Visibility.Visible)
                item.ShowChilds();
            if (vis == Visibility.Collapsed)
            {
                item.SetRowAndHolder(null, null);
                item.HideChilds();
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the TreeGridItem class
        /// </summary>
        public TreeGridItem()
        {
            _items = new TreeGridItemCollection(this);
            _items.CollectionChanged += ItemsCollectionChanged;
        }

        private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    ItemsSource.InserItemInSource(e.NewStartingIndex, ((TreeGridItem)e.NewItems[0]).Data);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    ItemsSource.RemoveItemFromSource(e.OldStartingIndex, ((TreeGridItem)e.OldItems[0]).Data);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
            }
        }
        #endregion

        private TreeHolder _holder;

        private void Attach()
        {
            _items.SetParentGrid(ParentGrid);
            SetBinding(ItemsSourceProperty, ParentGrid.Column.ItemsSource);
            ParentGrid.Refresh();
        }

        private void DeAttach()
        {
            _items.SetParentGrid(null);
            _row = null;
            ParentGrid.Refresh();
        }

        #region Events
        /// <summary>
        /// Occurs when the <see cref="IsExpanded"/> property changes from true to false
        /// </summary>
        public event RoutedEventHandler Collapsed;

        internal void InvokeCollapsed()
        {
            var handler = Collapsed;
            if (handler != null) handler(this, new RoutedEventArgs());
        }

        /// <summary>
        /// Occurs when the <see cref="IsExpanded"/> property changes from false to true
        /// </summary>
        public event RoutedEventHandler Expanded;

        internal void InvokeExpanded()
        {
            var handler = Expanded;
            if (handler != null) handler(this, new RoutedEventArgs());
        }

        /// <summary>
        /// Occurs when the <see cref="IsSelected"/> property changes from false to true
        /// </summary>
        public event RoutedEventHandler Selected;

        internal void InvokeSelected()
        {
            var handler = Selected;
            if (handler != null) handler(this, new RoutedEventArgs());
        }

        /// <summary>
        /// Occurs when the <see cref="IsSelected"/> property of a TreeViewItem changes from true to false
        /// </summary>
        public event RoutedEventHandler Unselected;

        internal void InvokeUnselected()
        {
            var handler = Unselected;
            if (handler != null) handler(this, new RoutedEventArgs());
        }
        #endregion

        #region ParentItem
        private TreeGridItem _parentItem;

        /// <summary>
        /// Gets the parent TreeGridItem for this item
        /// </summary>
        public TreeGridItem ParentItem { get { return _parentItem; } }
        
        internal void SetParentItem(TreeGridItem parentNode)
        {
            _parentItem = parentNode;
            if (_parentItem != null)
                Visibility = _parentItem.IsExpanded ? Visibility.Visible : Visibility.Collapsed;
        }
        #endregion

        #region ParentGrid
        private TreeGrid _parentGrid;

        /// <summary>
        /// Gets the parent <see cref="TreeGrid"/> for this item
        /// </summary>
        public TreeGrid ParentGrid { get { return _parentGrid; } }

        internal void SetParentGrid(TreeGrid grid)
        {
            if (_parentGrid == grid) return;
            if (_parentGrid != null) DeAttach();
            _parentGrid = grid;
            if (_parentGrid != null) Attach();
        }
        #endregion

        /// <summary>
        /// Gets the level of current TreeGridItem
        /// </summary>
        public int Level { get { return ParentItem == null ? 0 : ParentItem.Level + 1; } }

        /// <summary>
        /// Get or set data, represented in current TreeGridItem
        /// </summary>
        public object Data
        {
            get { return GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        #region Row
        private DataGridRow _row;

        /// <summary>
        /// Gets DataGridRow, that represented current TreeGridItem
        /// </summary>
        public DataGridRow Row { get { return _row; } }

        internal void SetRowAndHolder(DataGridRow row, TreeHolder holder)
        {
            _row = row;
            _holder = holder;
            if (_holder != null)
                _holder.Item = this;
            if (_row == null) return;
            _row.SetBinding(HeightProperty, new Binding("Height")
                                                {
                                                    Source = this,
                                                    Mode = BindingMode.TwoWay
                                                });
        }
        #endregion

        #region Items
        private readonly TreeGridItemCollection _items;

        /// <summary>
        /// Gets the collection of nested items
        /// </summary>
        public new TreeGridItemCollection Items { get { return _items; } }
        #endregion

        #region IsExpanded
        /// <summary>
        /// Gets or sets whether the TreeGridItem is expanded or collapsed
        /// </summary>
        public bool IsExpanded
        {
            get { return (bool) GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }

        private void UpdateExpandState(bool oldValue, bool newValue)
        {
            if (oldValue == newValue) return;
            if (newValue)
            {
                ShowChilds();
                if (ParentGrid != null)
                    ParentGrid.Refresh();
                InvokeExpanded();
            }
            else
            {
                HideChilds();
                if (ParentGrid != null)
                    ParentGrid.Refresh();
                InvokeCollapsed();
            }
        }
        #endregion

        #region IsSelected
        /// <summary>
        /// Gets or sets whether a TreeGridItem control is selected
        /// </summary>
        public bool IsSelected
        {
            get { return (bool) GetValue(IsSelectedProperty); }
            set { SetValue(IsSelectedProperty, value); }
        }
        #endregion

        /// <summary>
        /// Gets or sets the visibility of TreeGridItem
        /// </summary>
        public new Visibility Visibility
        {
            get { return (Visibility) GetValue(VisibilityProperty); }
            set { SetValue(VisibilityProperty, value); }
        }

        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    {
                        var itm = new TreeGridItem {Data = e.NewItems[0]};
                        if (ParentGrid != null && ParentGrid.Column != null && ParentGrid.Column.AutoExpand)
                            itm.IsExpanded = true;
                        if (!IsExpanded)
                            itm.Visibility = Visibility.Collapsed;
                        _items.InsertInt(e.NewStartingIndex, itm);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    {
                        var item = Items.FirstOrDefault(itm => itm.Data == e.OldItems[0]);
                        if (item == null) return;
                        Items.RemoveSilent(item);
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    {
                        _items.ClearInt();
                        foreach (var item in base.Items)
                        {
                            var itm = new TreeGridItem {Data = item};
                            if (ParentGrid != null && ParentGrid.Column != null && ParentGrid.Column.AutoExpand)
                                itm.IsExpanded = true;
                            if (!IsExpanded)
                                itm.Visibility = Visibility.Collapsed;
                            _items.AddInt(itm);
                        }
                    }
                    break;
            }
            if (_holder != null)
                _holder.Update();
        }

        private bool _isHidden;

        internal bool IsHidden
        {
            get { return _isHidden; }
            set
            {
                if (_isHidden == value) return;
                _isHidden = value;
                if (ParentGrid == null) return;
                ParentGrid.Refresh();
            }
        }

        private void ShowChilds()
        {
            foreach (var item in Items)
                item.Visibility = Visibility.Visible;
        }

        private void HideChilds()
        {
            foreach (var item in Items)
                item.Visibility = Visibility.Collapsed;
        }
    }
}